import sys
import os
import cv2
import numpy as np
from PyQt5.QtWidgets import (QApplication, QMainWindow, QTabWidget, QWidget, QVBoxLayout,
                             QHBoxLayout, QLabel, QPushButton, QFileDialog, QSlider,
                             QSpinBox, QDoubleSpinBox, QGroupBox, QRadioButton,
                             QButtonGroup, QComboBox, QMessageBox, QProgressBar,
                             QGridLayout, QSplitter, QScrollArea, QLineEdit, QAction,
                             QToolBar, QMenu, QDockWidget, QListWidget, QCheckBox)  # 添加QCheckBox导入
from PyQt5.QtGui import QPixmap, QImage, QPainter, QPen, QColor, QFont, QIcon
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QPoint, QRect, QSize
import math


class ImageProcessor:
    """图像处理核心类，封装所有图像处理功能"""

    def __init__(self):
        self.original_image = None
        self.processed_image = None
        self.current_path = None
        # 加载人脸检测器
        self.face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
        self.eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_eye.xml')
        self.faces_db = {}  # 人脸数据库

    def load_image(self, path):
        """加载图像"""
        self.current_path = path
        self.original_image = cv2.imread(path)
        self.processed_image = self.original_image.copy()
        return self.processed_image

    def save_image(self, path=None):
        """保存图像"""
        if path is None:
            path = self.current_path
        if self.processed_image is not None:
            cv2.imwrite(path, self.processed_image)
            return True
        return False

    def rotate_image(self, angle):
        """旋转图像"""
        if self.processed_image is not None:
            (h, w) = self.processed_image.shape[:2]
            center = (w // 2, h // 2)
            M = cv2.getRotationMatrix2D(center, angle, 1.0)
            self.processed_image = cv2.warpAffine(self.processed_image, M, (w, h))
            return self.processed_image
        return None

    def scale_image(self, scale):
        """缩放图像"""
        if self.processed_image is not None:
            width = int(self.processed_image.shape[1] * scale)
            height = int(self.processed_image.shape[0] * scale)
            dim = (width, height)
            self.processed_image = cv2.resize(self.processed_image, dim, interpolation=cv2.INTER_AREA)
            return self.processed_image
        return None

    def crop_image(self, x, y, w, h):
        """裁剪图像"""
        if self.processed_image is not None:
            self.processed_image = self.processed_image[y:y + h, x:x + w]
            return self.processed_image
        return None

    def adjust_brightness_contrast(self, brightness=0, contrast=0):
        """调整亮度和对比度"""
        if self.processed_image is not None:
            if brightness != 0:
                if brightness > 0:
                    shadow = brightness
                    highlight = 255
                else:
                    shadow = 0
                    highlight = 255 + brightness
                alpha_b = (highlight - shadow) / 255
                gamma_b = shadow

                buf = cv2.addWeighted(self.processed_image, alpha_b,
                                      self.processed_image, 0, gamma_b)
            else:
                buf = self.processed_image.copy()

            if contrast != 0:
                f = 131 * (contrast + 127) / (127 * (131 - contrast))
                alpha_c = f
                gamma_c = 127 * (1 - f)

                buf = cv2.addWeighted(buf, alpha_c,
                                      buf, 0, gamma_c)

            self.processed_image = buf
            return self.processed_image
        return None

    def adjust_saturation(self, value):
        """调整饱和度"""
        if self.processed_image is not None:
            hsv = cv2.cvtColor(self.processed_image, cv2.COLOR_BGR2HSV)
            h, s, v = cv2.split(hsv)
            s = np.clip(s * value, 0, 255).astype(hsv.dtype)
            hsv = cv2.merge([h, s, v])
            self.processed_image = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
            return self.processed_image
        return None

    def smooth_image(self, kernel_size=3, method='gaussian'):
        """平滑图像"""
        if self.processed_image is not None:
            if method == 'gaussian':
                self.processed_image = cv2.GaussianBlur(self.processed_image,
                                                        (kernel_size, kernel_size), 0)
            elif method == 'median':
                self.processed_image = cv2.medianBlur(self.processed_image, kernel_size)
            elif method == 'bilateral':
                self.processed_image = cv2.bilateralFilter(self.processed_image,
                                                           kernel_size, 75, 75)
            return self.processed_image
        return None

    def detect_shapes(self):
        """检测图像中的形状"""
        if self.processed_image is not None:
            output = self.processed_image.copy()
            gray = cv2.cvtColor(self.processed_image, cv2.COLOR_BGR2GRAY)
            _, thresh = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV)
            contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

            shapes = []
            for contour in contours:
                area = cv2.contourArea(contour)
                if area < 100:  # 过滤小面积
                    continue

                # 计算轮廓周长
                perimeter = cv2.arcLength(contour, True)
                # 多边形逼近
                approx = cv2.approxPolyDP(contour, 0.04 * perimeter, True)

                shape = "unidentified"
                if len(approx) == 3:
                    shape = "triangle"
                elif len(approx) == 4:
                    (x, y, w, h) = cv2.boundingRect(approx)
                    ar = w / float(h)
                    shape = "square" if 0.95 <= ar <= 1.05 else "rectangle"
                elif len(approx) > 4:
                    # 计算轮廓面积和凸包面积
                    area = cv2.contourArea(contour)
                    hull = cv2.convexHull(contour)
                    hull_area = cv2.contourArea(hull)
                    solidity = float(area) / hull_area

                    # 计算轮廓的圆形度
                    ((x, y), radius) = cv2.minEnclosingCircle(contour)
                    circle_area = math.pi * radius * radius
                    circularity = 4 * math.pi * (area / (perimeter * perimeter))

                    if circularity > 0.8 and abs(area - circle_area) < area * 0.2:
                        shape = "circle"
                    elif len(approx) == 5:
                        shape = "pentagon"
                    elif len(approx) == 6:
                        shape = "hexagon"
                    else:
                        shape = "polygon"

                # 绘制轮廓和形状名称
                cv2.drawContours(output, [contour], -1, (0, 255, 0), 2)
                cv2.putText(output, shape, (approx[0][0][0], approx[0][0][1]),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)

                shapes.append({
                    'type': shape,
                    'contour': contour,
                    'center': (approx[0][0][0], approx[0][0][1])
                })

            self.processed_image = output
            return self.processed_image, shapes
        return None, []

    def detect_faces(self):
        """检测人脸"""
        if self.processed_image is not None:
            gray = cv2.cvtColor(self.processed_image, cv2.COLOR_BGR2GRAY)
            faces = self.face_cascade.detectMultiScale(gray, 1.3, 5)

            output = self.processed_image.copy()
            detected_faces = []

            for (x, y, w, h) in faces:
                # 绘制人脸矩形框
                cv2.rectangle(output, (x, y), (x + w, y + h), (0, 255, 0), 2)

                # 尝试识别人脸
                name = "Unknown"
                face_roi = gray[y:y + h, x:x + w]

                # 在人脸区域内检测眼睛
                eyes = self.eye_cascade.detectMultiScale(face_roi)

                # 基于眼睛检测来验证是否为人脸
                if len(eyes) >= 1:
                    # 简化的人脸匹配（不使用face_recognition）
                    face_key = f"{x}_{y}_{w}_{h}"
                    if face_key in self.faces_db:
                        name = self.faces_db[face_key]

                # 显示人名
                cv2.putText(output, name, (x + 6, y + h - 6),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1)

                detected_faces.append({
                    'location': (x, y, w, h),
                    'name': name
                })

            self.processed_image = output
            return self.processed_image, detected_faces
        return None, []

    def register_face(self, name, image_path=None, image=None):
        """注册人脸到数据库"""
        if image_path:
            img = cv2.imread(image_path)
            if img is None:
                return False
        elif image is not None:
            img = image
        else:
            return False

        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        faces = self.face_cascade.detectMultiScale(gray, 1.3, 5)

        if len(faces) > 0:
            # 只取第一个检测到的人脸
            x, y, w, h = faces[0]
            face_key = f"{x}_{y}_{w}_{h}"
            self.faces_db[face_key] = name
            return True
        return False

    def stitch_images(self, images):
        """拼接多张图像"""
        if len(images) < 2:
            return None

        stitcher = cv2.Stitcher_create()
        status, result = stitcher.stitch(images)

        if status == cv2.Stitcher_OK:
            self.processed_image = result
            return self.processed_image
        else:
            print(f"Error during stitching: {status}")
            return None


class VideoProcessor(QThread):
    """视频处理线程"""
    frame_ready = pyqtSignal(np.ndarray)
    detection_ready = pyqtSignal(np.ndarray, list)
    finished = pyqtSignal()

    def __init__(self, source=0, processor=None, detection_type='none'):
        super().__init__()
        self.source = source
        self.processor = processor
        self.detection_type = detection_type
        self.cap = None
        self.running = False
        # 加载人脸检测器
        self.face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
        self.eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_eye.xml')

    def run(self):
        """线程运行函数"""
        self.cap = cv2.VideoCapture(self.source)
        if not self.cap.isOpened():
            self.finished.emit()
            return

        self.running = True
        while self.running:
            ret, frame = self.cap.read()
            if not ret:
                break

            if self.detection_type == 'faces':
                gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                faces = self.face_cascade.detectMultiScale(gray, 1.3, 5)

                output = frame.copy()
                detected_faces = []

                for (x, y, w, h) in faces:
                    cv2.rectangle(output, (x, y), (x + w, y + h), (0, 255, 0), 2)

                    name = "Unknown"
                    face_roi = gray[y:y + h, x:x + w]

                    # 在人脸区域内检测眼睛
                    eyes = self.eye_cascade.detectMultiScale(face_roi)

                    # 基于眼睛检测来验证是否为人脸
                    if len(eyes) >= 1:
                        # 简化的人脸匹配（不使用face_recognition）
                        face_key = f"{x}_{y}_{w}_{h}"
                        if self.processor and face_key in self.processor.faces_db:
                            name = self.processor.faces_db[face_key]

                    cv2.putText(output, name, (x + 6, y + h - 6),
                                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1)

                    detected_faces.append({
                        'location': (x, y, w, h),
                        'name': name
                    })

                self.detection_ready.emit(output, detected_faces)

            elif self.detection_type == 'shapes':
                output = frame.copy()
                gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                _, thresh = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV)
                contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

                shapes = []
                for contour in contours:
                    area = cv2.contourArea(contour)
                    if area < 100:
                        continue

                    perimeter = cv2.arcLength(contour, True)
                    approx = cv2.approxPolyDP(contour, 0.04 * perimeter, True)

                    shape = "unidentified"
                    if len(approx) == 3:
                        shape = "triangle"
                    elif len(approx) == 4:
                        (x, y, w, h) = cv2.boundingRect(approx)
                        ar = w / float(h)
                        shape = "square" if 0.95 <= ar <= 1.05 else "rectangle"
                    elif len(approx) > 4:
                        area = cv2.contourArea(contour)
                        hull = cv2.convexHull(contour)
                        hull_area = cv2.contourArea(hull)
                        solidity = float(area) / hull_area

                        ((x, y), radius) = cv2.minEnclosingCircle(contour)
                        circle_area = math.pi * radius * radius
                        circularity = 4 * math.pi * (area / (perimeter * perimeter))

                        if circularity > 0.8 and abs(area - circle_area) < area * 0.2:
                            shape = "circle"
                        elif len(approx) == 5:
                            shape = "pentagon"
                        elif len(approx) == 6:
                            shape = "hexagon"
                        else:
                            shape = "polygon"

                    cv2.drawContours(output, [contour], -1, (0, 255, 0), 2)
                    cv2.putText(output, shape, (approx[0][0][0], approx[0][0][1]),
                                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)

                    shapes.append({
                        'type': shape,
                        'center': (approx[0][0][0], approx[0][0][1])
                    })

                self.detection_ready.emit(output, shapes)

            else:
                self.frame_ready.emit(frame)

        self.cap.release()
        self.finished.emit()

    def stop(self):
        """停止线程"""
        self.running = False
        self.wait()


class ImageWidget(QWidget):
    """图像显示控件"""

    def __init__(self, parent=None):
        super().__init__(parent)
        self.image = None
        self.setMinimumSize(1, 1)
        self.scale_factor = 1.0
        self.dragging = False
        self.last_pos = QPoint()
        self.scroll_area = None
        self.crop_mode = False
        self.crop_rect = QRect()
        self.selecting = False

    def set_image(self, image):
        """设置要显示的图像"""
        self.image = image
        self.update()

    def set_scroll_area(self, scroll_area):
        """设置滚动区域"""
        self.scroll_area = scroll_area

    def set_crop_mode(self, enabled):
        """设置裁剪模式"""
        self.crop_mode = enabled
        if not enabled:
            self.crop_rect = QRect()
            self.selecting = False
        self.update()

    def mousePressEvent(self, event):
        """鼠标按下事件"""
        if self.crop_mode and self.image and event.button() == Qt.LeftButton:
            self.selecting = True
            self.crop_rect.setTopLeft(event.pos())
            self.crop_rect.setBottomRight(event.pos())
            self.update()
        elif event.button() == Qt.LeftButton:
            self.dragging = True
            self.last_pos = event.pos()

    def mouseMoveEvent(self, event):
        """鼠标移动事件"""
        if self.crop_mode and self.image and self.selecting:
            self.crop_rect.setBottomRight(event.pos())
            self.update()
        elif self.dragging and self.scroll_area:
            delta = event.pos() - self.last_pos
            self.last_pos = event.pos()

            # 滚动视图
            horizontal_bar = self.scroll_area.horizontalScrollBar()
            vertical_bar = self.scroll_area.verticalScrollBar()

            horizontal_bar.setValue(horizontal_bar.value() - delta.x())
            vertical_bar.setValue(vertical_bar.value() - delta.y())

    def mouseReleaseEvent(self, event):
        """鼠标释放事件"""
        if self.crop_mode and self.image and event.button() == Qt.LeftButton and self.selecting:
            self.selecting = False
            # 确保矩形的左上右下点正确
            if self.crop_rect.width() < 0:
                self.crop_rect.setLeft(self.crop_rect.right())
                self.crop_rect.setRight(self.crop_rect.left() + self.crop_rect.width())
            if self.crop_rect.height() < 0:
                self.crop_rect.setTop(self.crop_rect.bottom())
                self.crop_rect.setBottom(self.crop_rect.top() + self.crop_rect.height())
            self.update()
        elif event.button() == Qt.LeftButton:
            self.dragging = False

    def wheelEvent(self, event):
        """鼠标滚轮事件 - 缩放图像"""
        if self.image:
            delta = event.angleDelta().y()
            if delta > 0:
                self.scale_factor *= 1.1
            else:
                self.scale_factor *= 0.9
            self.update()

    def paintEvent(self, event):
        """绘制事件"""
        painter = QPainter(self)
        if self.image:
            # 绘制图像
            scaled_image = self.image.scaled(
                self.image.width() * self.scale_factor,
                self.image.height() * self.scale_factor,
                Qt.KeepAspectRatio, Qt.SmoothTransformation)

            painter.drawImage(QPoint(0, 0), scaled_image)

            # 如果在裁剪模式下，绘制选择框
            if self.crop_mode and not self.crop_rect.isEmpty():
                painter.setPen(QPen(QColor(255, 0, 0), 2, Qt.DashLine))
                painter.drawRect(self.crop_rect)

        else:
            # 绘制占位文本
            painter.setPen(QPen(Qt.black))
            painter.setFont(QFont("Arial", 12))
            painter.drawText(self.rect(), Qt.AlignCenter, "No image loaded")

    def get_crop_rect(self):
        """获取裁剪矩形（考虑缩放）"""
        if self.crop_mode and not self.crop_rect.isEmpty():
            return QRect(
                int(self.crop_rect.x() / self.scale_factor),
                int(self.crop_rect.y() / self.scale_factor),
                int(self.crop_rect.width() / self.scale_factor),
                int(self.crop_rect.height() / self.scale_factor)
            )
        return QRect()


class MainWindow(QMainWindow):
    """主窗口"""

    def __init__(self):
        super().__init__()
        self.setWindowTitle("计算机视觉平台")
        self.setGeometry(100, 100, 1200, 800)

        self.processor = ImageProcessor()
        self.video_processor = None

        self.create_menu()
        self.create_toolbar()
        self.create_tabs()
        self.create_dock_widgets()

        # 连接信号和槽
        self.image_tab.load_button.clicked.connect(self.load_image)
        self.image_tab.save_button.clicked.connect(self.save_image)
        self.image_tab.rotate_button.clicked.connect(self.rotate_image)
        self.image_tab.scale_button.clicked.connect(self.scale_image)
        self.image_tab.crop_button.clicked.connect(self.toggle_crop_mode)
        self.image_tab.crop_apply_button.clicked.connect(self.apply_crop)
        self.image_tab.brightness_slider.valueChanged.connect(self.adjust_brightness)
        self.image_tab.contrast_slider.valueChanged.connect(self.adjust_contrast)
        self.image_tab.saturation_slider.valueChanged.connect(self.adjust_saturation)
        self.image_tab.smooth_button.clicked.connect(self.smooth_image)
        self.image_tab.shape_detection_button.clicked.connect(self.detect_shapes)
        self.image_tab.face_detection_button.clicked.connect(self.detect_faces)
        self.image_tab.register_face_button.clicked.connect(self.register_face)
        self.image_tab.stitch_button.clicked.connect(self.stitch_images)

        self.video_tab.load_video_button.clicked.connect(self.load_video)
        self.video_tab.start_camera_button.clicked.connect(self.start_camera)
        self.video_tab.stop_button.clicked.connect(self.stop_video)
        self.video_tab.face_detection_checkbox.toggled.connect(self.toggle_face_detection)
        self.video_tab.shape_detection_checkbox.toggled.connect(self.toggle_shape_detection)

        self.faces_list.itemDoubleClicked.connect(self.remove_face)

    def create_menu(self):
        """创建菜单栏"""
        menubar = self.menuBar()

        # 文件菜单
        file_menu = menubar.addMenu("文件")

        load_action = QAction("加载图像", self)
        load_action.setShortcut("Ctrl+O")
        load_action.triggered.connect(self.load_image)
        file_menu.addAction(load_action)

        save_action = QAction("保存图像", self)
        save_action.setShortcut("Ctrl+S")
        save_action.triggered.connect(self.save_image)
        file_menu.addAction(save_action)

        exit_action = QAction("退出", self)
        exit_action.setShortcut("Ctrl+Q")
        exit_action.triggered.connect(self.close)
        file_menu.addAction(exit_action)

        # 编辑菜单
        edit_menu = menubar.addMenu("编辑")

        undo_action = QAction("撤销", self)
        undo_action.setShortcut("Ctrl+Z")
        edit_menu.addAction(undo_action)

        redo_action = QAction("重做", self)
        redo_action.setShortcut("Ctrl+Y")
        edit_menu.addAction(redo_action)

        # 帮助菜单
        help_menu = menubar.addMenu("帮助")

        about_action = QAction("关于", self)
        about_action.triggered.connect(self.show_about)
        help_menu.addAction(about_action)

    def create_toolbar(self):
        """创建工具栏"""
        toolbar = QToolBar("工具栏")
        self.addToolBar(toolbar)

        load_action = QAction(QIcon(":/icons/open.png"), "加载图像", self)
        load_action.triggered.connect(self.load_image)
        toolbar.addAction(load_action)

        save_action = QAction(QIcon(":/icons/save.png"), "保存图像", self)
        save_action.triggered.connect(self.save_image)
        toolbar.addAction(save_action)

        toolbar.addSeparator()

        undo_action = QAction(QIcon(":/icons/undo.png"), "撤销", self)
        toolbar.addAction(undo_action)

        redo_action = QAction(QIcon(":/icons/redo.png"), "重做", self)
        toolbar.addAction(redo_action)

    def create_tabs(self):
        """创建标签页"""
        self.tabs = QTabWidget()

        # 图像标签页
        self.image_tab = QWidget()
        self.create_image_tab()
        self.tabs.addTab(self.image_tab, "图像处理")

        # 视频标签页
        self.video_tab = QWidget()
        self.create_video_tab()
        self.tabs.addTab(self.video_tab, "视频处理")

        self.setCentralWidget(self.tabs)

    def create_image_tab(self):
        """创建图像处理标签页"""
        # 主布局
        main_layout = QHBoxLayout(self.image_tab)

        # 左侧控制面板
        control_panel = QWidget()
        control_panel.setMaximumWidth(300)
        control_layout = QVBoxLayout(control_panel)

        # 文件操作组
        file_group = QGroupBox("文件操作")
        file_layout = QVBoxLayout(file_group)

        self.image_tab.load_button = QPushButton("加载图像")
        self.image_tab.save_button = QPushButton("保存图像")

        file_layout.addWidget(self.image_tab.load_button)
        file_layout.addWidget(self.image_tab.save_button)

        control_layout.addWidget(file_group)

        # 基本编辑组
        edit_group = QGroupBox("基本编辑")
        edit_layout = QVBoxLayout(edit_group)

        # 旋转控制
        rotate_layout = QHBoxLayout()
        self.image_tab.rotate_angle = QSpinBox()
        self.image_tab.rotate_angle.setRange(-359, 359)
        self.image_tab.rotate_angle.setValue(0)
        self.image_tab.rotate_button = QPushButton("旋转")

        rotate_layout.addWidget(QLabel("角度:"))
        rotate_layout.addWidget(self.image_tab.rotate_angle)
        rotate_layout.addWidget(self.image_tab.rotate_button)

        # 缩放控制
        scale_layout = QHBoxLayout()
        self.image_tab.scale_factor = QDoubleSpinBox()
        self.image_tab.scale_factor.setRange(0.1, 10.0)
        self.image_tab.scale_factor.setValue(1.0)
        self.image_tab.scale_factor.setSingleStep(0.1)
        self.image_tab.scale_button = QPushButton("缩放")

        scale_layout.addWidget(QLabel("比例:"))
        scale_layout.addWidget(self.image_tab.scale_factor)
        scale_layout.addWidget(self.image_tab.scale_button)

        # 裁剪控制
        crop_layout = QVBoxLayout()
        self.image_tab.crop_button = QPushButton("裁剪模式")
        self.image_tab.crop_apply_button = QPushButton("应用裁剪")
        self.image_tab.crop_apply_button.setEnabled(False)

        crop_layout.addWidget(self.image_tab.crop_button)
        crop_layout.addWidget(self.image_tab.crop_apply_button)

        edit_layout.addLayout(rotate_layout)
        edit_layout.addLayout(scale_layout)
        edit_layout.addLayout(crop_layout)

        control_layout.addWidget(edit_group)

        # 色彩调整组
        color_group = QGroupBox("色彩调整")
        color_layout = QVBoxLayout(color_group)

        # 亮度控制
        brightness_layout = QHBoxLayout()
        self.image_tab.brightness_label = QLabel("亮度: 0")
        self.image_tab.brightness_slider = QSlider(Qt.Horizontal)
        self.image_tab.brightness_slider.setRange(-100, 100)
        self.image_tab.brightness_slider.setValue(0)

        brightness_layout.addWidget(self.image_tab.brightness_slider)
        brightness_layout.addWidget(self.image_tab.brightness_label)

        # 对比度控制
        contrast_layout = QHBoxLayout()
        self.image_tab.contrast_label = QLabel("对比度: 0")
        self.image_tab.contrast_slider = QSlider(Qt.Horizontal)
        self.image_tab.contrast_slider.setRange(-100, 100)
        self.image_tab.contrast_slider.setValue(0)

        contrast_layout.addWidget(self.image_tab.contrast_slider)
        contrast_layout.addWidget(self.image_tab.contrast_label)

        # 饱和度控制
        saturation_layout = QHBoxLayout()
        self.image_tab.saturation_label = QLabel("饱和度: 1.0")
        self.image_tab.saturation_slider = QSlider(Qt.Horizontal)
        self.image_tab.saturation_slider.setRange(0, 200)
        self.image_tab.saturation_slider.setValue(100)

        saturation_layout.addWidget(self.image_tab.saturation_slider)
        saturation_layout.addWidget(self.image_tab.saturation_label)

        color_layout.addLayout(brightness_layout)
        color_layout.addLayout(contrast_layout)
        color_layout.addLayout(saturation_layout)

        control_layout.addWidget(color_group)

        # 图像处理组
        process_group = QGroupBox("图像处理")
        process_layout = QVBoxLayout(process_group)

        # 平滑处理
        smooth_layout = QHBoxLayout()
        self.image_tab.smooth_kernel = QSpinBox()
        self.image_tab.smooth_kernel.setRange(1, 21)
        self.image_tab.smooth_kernel.setValue(3)
        self.image_tab.smooth_kernel.setSingleStep(2)
        self.image_tab.smooth_type = QComboBox()
        self.image_tab.smooth_type.addItems(["高斯模糊", "中值滤波", "双边滤波"])
        self.image_tab.smooth_button = QPushButton("平滑处理")

        smooth_layout.addWidget(self.image_tab.smooth_kernel)
        smooth_layout.addWidget(self.image_tab.smooth_type)
        smooth_layout.addWidget(self.image_tab.smooth_button)

        # 形状检测
        self.image_tab.shape_detection_button = QPushButton("形状检测")

        # 人脸检测
        face_layout = QVBoxLayout()
        self.image_tab.face_detection_button = QPushButton("人脸检测")
        self.image_tab.register_face_button = QPushButton("注册人脸")

        face_layout.addWidget(self.image_tab.face_detection_button)
        face_layout.addWidget(self.image_tab.register_face_button)

        # 图像拼接
        self.image_tab.stitch_button = QPushButton("图像拼接")

        process_layout.addLayout(smooth_layout)
        process_layout.addWidget(self.image_tab.shape_detection_button)
        process_layout.addLayout(face_layout)
        process_layout.addWidget(self.image_tab.stitch_button)

        control_layout.addWidget(process_group)

        control_layout.addStretch()

        # 右侧显示区域
        display_area = QWidget()
        display_layout = QVBoxLayout(display_area)

        # 图像显示区域
        self.image_scroll_area = QScrollArea()
        self.image_widget = ImageWidget()
        self.image_widget.set_scroll_area(self.image_scroll_area)
        self.image_scroll_area.setWidget(self.image_widget)
        self.image_scroll_area.setWidgetResizable(True)

        # 结果信息区域
        self.result_info = QLabel("处理结果信息将显示在这里")
        self.result_info.setWordWrap(True)
        self.result_info.setMinimumHeight(50)

        display_layout.addWidget(self.image_scroll_area, 1)
        display_layout.addWidget(self.result_info)

        main_layout.addWidget(control_panel)
        main_layout.addWidget(display_area, 1)

    def create_video_tab(self):
        """创建视频处理标签页"""
        # 主布局
        main_layout = QVBoxLayout(self.video_tab)

        # 控制区域
        control_layout = QHBoxLayout()

        self.video_tab.load_video_button = QPushButton("加载视频")
        self.video_tab.start_camera_button = QPushButton("开启摄像头")
        self.video_tab.stop_button = QPushButton("停止")
        self.video_tab.stop_button.setEnabled(False)

        self.video_tab.face_detection_checkbox = QCheckBox("人脸检测")  # 这里使用已导入的QCheckBox
        self.video_tab.shape_detection_checkbox = QCheckBox("形状检测")  # 这里使用已导入的QCheckBox

        control_layout.addWidget(self.video_tab.load_video_button)
        control_layout.addWidget(self.video_tab.start_camera_button)
        control_layout.addWidget(self.video_tab.stop_button)
        control_layout.addStretch()
        control_layout.addWidget(self.video_tab.face_detection_checkbox)
        control_layout.addWidget(self.video_tab.shape_detection_checkbox)

        # 视频显示区域
        self.video_scroll_area = QScrollArea()
        self.video_widget = ImageWidget()
        self.video_widget.set_scroll_area(self.video_scroll_area)
        self.video_scroll_area.setWidget(self.video_widget)
        self.video_scroll_area.setWidgetResizable(True)

        # 结果信息区域
        self.video_result_info = QLabel("视频处理结果信息将显示在这里")
        self.video_result_info.setWordWrap(True)
        self.video_result_info.setMinimumHeight(50)

        main_layout.addLayout(control_layout)
        main_layout.addWidget(self.video_scroll_area, 1)
        main_layout.addWidget(self.video_result_info)

    def create_dock_widgets(self):
        """创建停靠窗口"""
        # 人脸数据库停靠窗口
        faces_dock = QDockWidget("人脸数据库", self)
        faces_dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

        self.faces_list = QListWidget()
        faces_dock.setWidget(self.faces_list)

        self.addDockWidget(Qt.RightDockWidgetArea, faces_dock)

    def load_image(self):
        """加载图像"""
        options = QFileDialog.Options()
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择图像", "", "图像文件 (*.png *.jpg *.jpeg *.bmp *.gif);;所有文件 (*)", options=options)

        if file_path:
            image = self.processor.load_image(file_path)
            if image is not None:
                self.display_image(image)
                self.result_info.setText(f"已加载图像: {os.path.basename(file_path)}")

    def display_image(self, image):
        """显示图像"""
        if len(image.shape) == 3:
            rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        else:
            rgb_image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)

        h, w, ch = rgb_image.shape
        bytes_per_line = ch * w
        q_image = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)

        if self.tabs.currentIndex() == 0:  # 图像标签页
            self.image_widget.set_image(q_image)
        else:  # 视频标签页
            self.video_widget.set_image(q_image)

    def save_image(self):
        """保存图像"""
        if self.processor.processed_image is not None:
            options = QFileDialog.Options()
            file_path, _ = QFileDialog.getSaveFileName(
                self, "保存图像", "", "图像文件 (*.png *.jpg *.jpeg *.bmp);;所有文件 (*)", options=options)

            if file_path:
                if self.processor.save_image(file_path):
                    self.result_info.setText(f"已保存图像: {os.path.basename(file_path)}")
                else:
                    self.result_info.setText("保存图像失败")

    def rotate_image(self):
        """旋转图像"""
        angle = self.image_tab.rotate_angle.value()
        if self.processor.processed_image is not None:
            image = self.processor.rotate_image(angle)
            self.display_image(image)
            self.result_info.setText(f"已旋转图像: {angle}度")

    def scale_image(self):
        """缩放图像"""
        scale = self.image_tab.scale_factor.value()
        if self.processor.processed_image is not None:
            image = self.processor.scale_image(scale)
            self.display_image(image)
            self.result_info.setText(f"已缩放图像: {scale}倍")

    def toggle_crop_mode(self):
        """切换裁剪模式"""
        if self.processor.processed_image is not None:
            current_mode = self.image_widget.crop_mode
            self.image_widget.set_crop_mode(not current_mode)
            self.image_tab.crop_apply_button.setEnabled(not current_mode)
            if not current_mode:
                self.result_info.setText("已进入裁剪模式，请在图像上选择区域")
            else:
                self.result_info.setText("已退出裁剪模式")

    def apply_crop(self):
        """应用裁剪"""
        crop_rect = self.image_widget.get_crop_rect()
        if not crop_rect.isEmpty() and self.processor.processed_image is not None:
            x, y, w, h = crop_rect.x(), crop_rect.y(), crop_rect.width(), crop_rect.height()
            image = self.processor.crop_image(x, y, w, h)
            self.display_image(image)
            self.image_widget.set_crop_mode(False)
            self.image_tab.crop_apply_button.setEnabled(False)
            self.result_info.setText(f"已裁剪图像: 区域({x}, {y}, {w}, {h})")

    def adjust_brightness(self, value):
        """调整亮度"""
        self.image_tab.brightness_label.setText(f"亮度: {value}")
        if self.processor.processed_image is not None:
            contrast = self.image_tab.contrast_slider.value()
            image = self.processor.adjust_brightness_contrast(value, contrast)
            self.display_image(image)
            self.result_info.setText(f"已调整亮度: {value}")

    def adjust_contrast(self, value):
        """调整对比度"""
        self.image_tab.contrast_label.setText(f"对比度: {value}")
        if self.processor.processed_image is not None:
            brightness = self.image_tab.brightness_slider.value()
            image = self.processor.adjust_brightness_contrast(brightness, value)
            self.display_image(image)
            self.result_info.setText(f"已调整对比度: {value}")

    def adjust_saturation(self, value):
        """调整饱和度"""
        # 将0-200的范围映射到0.0-2.0
        saturation = value / 100.0
        self.image_tab.saturation_label.setText(f"饱和度: {saturation:.1f}")
        if self.processor.processed_image is not None:
            image = self.processor.adjust_saturation(saturation)
            self.display_image(image)
            self.result_info.setText(f"已调整饱和度: {saturation:.1f}")

    def smooth_image(self):
        """平滑图像"""
        kernel_size = self.image_tab.smooth_kernel.value()
        smooth_type = self.image_tab.smooth_type.currentText()

        method_map = {
            "高斯模糊": "gaussian",
            "中值滤波": "median",
            "双边滤波": "bilateral"
        }

        method = method_map.get(smooth_type, "gaussian")

        if self.processor.processed_image is not None:
            image = self.processor.smooth_image(kernel_size, method)
            self.display_image(image)
            self.result_info.setText(f"已应用{smooth_type}: 核大小{kernel_size}")

    def detect_shapes(self):
        """检测形状"""
        if self.processor.processed_image is not None:
            image, shapes = self.processor.detect_shapes()
            self.display_image(image)
            shape_counts = {
                "triangle": 0,
                "square": 0,
                "rectangle": 0,
                "circle": 0,
                "pentagon": 0,
                "hexagon": 0,
                "polygon": 0,
                "unidentified": 0
            }

            for shape in shapes:
                shape_counts[shape['type']] += 1

            result_text = "形状检测结果:\n"
            for shape_type, count in shape_counts.items():
                if count > 0:
                    result_text += f"{shape_type}: {count}个\n"

            self.result_info.setText(result_text.strip())

    def detect_faces(self):
        """检测人脸"""
        if self.processor.processed_image is not None:
            image, faces = self.processor.detect_faces()
            self.display_image(image)

            result_text = f"人脸检测结果: 共检测到{len(faces)}个人脸\n"
            for i, face in enumerate(faces):
                result_text += f"人脸 {i + 1}: {face['name']}\n"

            self.result_info.setText(result_text.strip())

    def register_face(self):
        """注册人脸"""
        options = QFileDialog.Options()
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择人脸图像", "", "图像文件 (*.png *.jpg *.jpeg *.bmp);;所有文件 (*)", options=options)

        if file_path:
            name, ok = QLineEdit.getText(self, "注册人脸", "请输入此人姓名:")
            if ok and name:
                if self.processor.register_face(name, file_path):
                    self.faces_list.addItem(name)
                    self.result_info.setText(f"已注册人脸: {name}")
                else:
                    self.result_info.setText("注册人脸失败，未检测到人脸")

    def remove_face(self, item):
        """移除人脸"""
        name = item.text()
        # 由于使用了简化的人脸匹配，这里需要遍历查找并移除
        keys_to_remove = [key for key, value in self.processor.faces_db.items() if value == name]
        for key in keys_to_remove:
            del self.processor.faces_db[key]

        self.faces_list.takeItem(self.faces_list.row(item))
        self.result_info.setText(f"已移除人脸: {name}")

    def stitch_images(self):
        """图像拼接"""
        options = QFileDialog.Options()
        file_paths, _ = QFileDialog.getOpenFileNames(
            self, "选择要拼接的图像", "", "图像文件 (*.png *.jpg *.jpeg *.bmp);;所有文件 (*)", options=options)

        if len(file_paths) >= 2:
            images = []
            for path in file_paths:
                img = cv2.imread(path)
                if img is not None:
                    images.append(img)

            if images:
                result = self.processor.stitch_images(images)
                if result is not None:
                    self.display_image(result)
                    self.result_info.setText(f"已成功拼接{len(images)}张图像")
                else:
                    self.result_info.setText("图像拼接失败")
            else:
                self.result_info.setText("无法加载选择的图像")

    def load_video(self):
        """加载视频"""
        options = QFileDialog.Options()
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择视频", "", "视频文件 (*.mp4 *.avi *.mov *.mkv);;所有文件 (*)", options=options)

        if file_path:
            self.start_video_processor(file_path)

    def start_camera(self):
        """开启摄像头"""
        self.start_video_processor(0)

    def start_video_processor(self, source):
        """启动视频处理线程"""
        if self.video_processor and self.video_processor.isRunning():
            self.video_processor.stop()

        self.video_processor = VideoProcessor(
            source=source,
            processor=self.processor,
            detection_type='none'
        )

        self.video_processor.frame_ready.connect(self.update_video_frame)
        self.video_processor.detection_ready.connect(self.update_video_detection)
        self.video_processor.finished.connect(self.video_processing_finished)

        self.video_processor.start()
        self.video_tab.stop_button.setEnabled(True)
        self.video_tab.load_video_button.setEnabled(False)
        self.video_tab.start_camera_button.setEnabled(False)

        if source == 0:
            self.video_result_info.setText("已开启摄像头")
        else:
            self.video_result_info.setText(f"已加载视频: {os.path.basename(source)}")

    def stop_video(self):
        """停止视频处理"""
        if self.video_processor and self.video_processor.isRunning():
            self.video_processor.stop()
            self.video_processing_finished()

    def update_video_frame(self, frame):
        """更新视频帧"""
        self.display_image(frame)

    def update_video_detection(self, frame, results):
        """更新视频检测结果"""
        self.display_image(frame)

        if self.video_processor.detection_type == 'faces':
            result_text = f"人脸检测结果: 共检测到{len(results)}个人脸\n"
            for i, face in enumerate(results):
                result_text += f"人脸 {i + 1}: {face['name']}\n"
        elif self.video_processor.detection_type == 'shapes':
            shape_counts = {
                "triangle": 0,
                "square": 0,
                "rectangle": 0,
                "circle": 0,
                "pentagon": 0,
                "hexagon": 0,
                "polygon": 0,
                "unidentified": 0
            }

            for shape in results:
                shape_counts[shape['type']] += 1

            result_text = "形状检测结果:\n"
            for shape_type, count in shape_counts.items():
                if count > 0:
                    result_text += f"{shape_type}: {count}个\n"
        else:
            result_text = "处理中..."

        self.video_result_info.setText(result_text.strip())

    def video_processing_finished(self):
        """视频处理完成"""
        self.video_tab.stop_button.setEnabled(False)
        self.video_tab.load_video_button.setEnabled(True)
        self.video_tab.start_camera_button.setEnabled(True)
        self.video_result_info.setText("视频处理已停止")

    def toggle_face_detection(self, checked):
        """切换人脸检测"""
        if checked:
            self.video_tab.shape_detection_checkbox.setChecked(False)
            if self.video_processor and self.video_processor.isRunning():
                self.video_processor.detection_type = 'faces'
        elif self.video_processor and not self.video_tab.shape_detection_checkbox.isChecked():
            self.video_processor.detection_type = 'none'

    def toggle_shape_detection(self, checked):
        """切换形状检测"""
        if checked:
            self.video_tab.face_detection_checkbox.setChecked(False)
            if self.video_processor and self.video_processor.isRunning():
                self.video_processor.detection_type = 'shapes'
        elif self.video_processor and not self.video_tab.face_detection_checkbox.isChecked():
            self.video_processor.detection_type = 'none'

    def show_about(self):
        """显示关于对话框"""
        QMessageBox.about(self, "关于", "图像处理与计算机视觉平台\n\n"
                                      "基于PyQt5和OpenCV开发\n"
                                      "支持图像处理、形状识别和人脸识别等功能")


if __name__ == "__main__":
    # 确保中文显示正常
    import matplotlib

    matplotlib.use('Agg')

    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())